﻿using System;
using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using System.Net.Mail;
using System.Web;
using System.Net;
using System.Collections;

namespace MyspaceToolkit
{
    /// <summary> 
    /// HttpPostUtility is a helper class that handles the posting of the api variables to the myspace service endpoint. 
    /// </summary> 
    /// <remarks> 
    /// This class will eventually be called by methods inside of the MyspaceClient class and will not be used directly. 
    /// </remarks> 
    public class HttpPostUtility
    {

        /// <summary> 
        /// RequestMethod type determines which API Method should be called. 
        /// </summary> 
        /// <remarks> 
        /// </remarks> 
        public enum RequestMethod
        {
            GetProfile = 0,
            GetBasicProfile = 1,
            GetFriends = 2,
            GetFriendById = 3,
            GetAlbums = 4,
            GetAlbumById = 5,
            GetAlbumPhotosById = 6,
            GetUserPhotos = 7,
            GetPhotoById = 8,
            GetUserInterests = 9,
            GetUserDetails = 10,
            GetUserVideos = 11,
            GetVideoById = 12,
            GetUserStatus = 13,
            GetUserMood = 14
        }

        /// <summary> 
        /// RequestMode determines the method for the request. Currently this value is examined upon request, and by default querystrings are used to make all requests. 
        /// </summary> 
        /// <remarks> 
        /// </remarks> 
        public enum RequestMode
        {
            AuthHeaders,
            QueryString
        }

        /// <summary> 
        /// ResponseType determines the content type of the response. There will be a parser class to parse either the xml or json response 
        /// and serialize the values into an object. 
        /// </summary> 
        /// <remarks> 
        /// </remarks> 
        public enum ResponseType
        {
            JSON,
            XML
        }

        /// <summary> 
        /// MyspaceRequest is used as the base request class to contain important values required to make a successful API request. 
        /// </summary> 
        /// <remarks> 
        /// </remarks> 
        public class MyspaceRequest
        {

            private RequestMethod _requestMethod;
            private RequestMode _requestMode;
            private ResponseType _responseType;
            private string _assetId;
            private string _consumerKey;
            private string _consumerSecret;
            private string _token;
            private string _tokenSecret;
            private string _baseApiUrlHost;
            private string _apiVersion;
            private string _userId;
            private Hashtable _urlParameters = new Hashtable();
            private string _pageNumber;
            private string _pageSize;
            private ListType _listType;

            /// <summary> 
            /// ListType determines the type of response when executing the GetFriends method. 
            /// </summary> 
            /// <remarks> 
            /// </remarks> 
            public enum ListType
            {
                none,
                top,
                online
            }

            /// <summary> 
            /// APIVersion is what version of the myspace API you would like to use. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string APIversion
            {
                get { return _apiVersion; }
                set { _apiVersion = value; }
            }

            /// <summary> 
            /// AssetID is used for assets such as: PhotoId, Friend/User ID, VideoId, AlbumId etc. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string AssetID
            {
                get { return _assetId; }
                set { _assetId = value; }
            }

            /// <summary> 
            /// BaseApiUrlHost is used for the myspace service endpoint address to make calls. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string BaseApiUrlHost
            {
                get { return _baseApiUrlHost; }
                set { _baseApiUrlHost = value; }
            }

            /// <summary> 
            /// ConsumerKey is the key provided to you once you have created an application inside of the Myspace Developer Platform (MDP). 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string ConsumerKey
            {
                get { return _consumerKey; }
                set { _consumerKey = value; }
            }

            /// <summary> 
            /// ConsumerSecret is the secret value provided to you ocne you have created an application inside of the MDP. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string ConsumerSecret
            {
                get { return _consumerSecret; }
                set { _consumerSecret = value; }
            }

            /// <summary> 
            /// ResponseListType is used specifically for when calling GetFriends to determine the type of friends to return. 
            /// <example> 
            /// ResponseListType rlt = new ResponseListType(); 
            /// rlt.none | rlt.top | rlt.online; 
            /// </example> 
            /// </summary> 
            /// <value>ListType</value> 
            /// <returns>ListType</returns> 
            /// <remarks></remarks> 
            public ListType ResponseListType
            {
                get { return _listType; }
                set { _listType = value; }
            }

            /// <summary> 
            /// ResponsePageNumber is used specifically for when calling GetFriends to specify the page number to return. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string ResponsePageNumber
            {
                get { return _pageNumber; }
                set { _pageNumber = value; }
            }

            /// <summary> 
            /// ResponsePageNumber is used specifically for when calling GetFriends to specify the page size to be returned. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string ResponsePageSize
            {
                get { return _pageSize; }
                set { _pageSize = value; }
            }

            /// <summary> 
            /// RequestMethod is the specific endpoint method to be invoked to return a result. Such as GetFriends, GetPhotoAlbums etc. 
            /// </summary> 
            /// <value>RequestMethod</value> 
            /// <returns>RequestMethod</returns> 
            /// <remarks></remarks> 
            public RequestMethod RequestMethod
            {
                get { return _requestMethod; }
                set { _requestMethod = value; }
            }

            /// <summary> 
            /// RequestMode is the mode for the request to go out. Currently this property is ignored and QueryStrings is always used. 
            /// This will be implemented in the next version of this toolkit. 
            /// </summary> 
            /// <value>RequestMode - AuthHeaders | Querystring</value> 
            /// <returns>RequestMode</returns> 
            /// <remarks></remarks> 
            public RequestMode RequestMode
            {
                get { return _requestMode; }
                set { _requestMode = value; }
            }

            /// <summary> 
            /// ResponseType is used to the determine the response content from an API request. 
            /// </summary> 
            /// <value>ResponseType - XML | JSON</value> 
            /// <returns>ResponseType</returns> 
            /// <remarks></remarks> 
            public ResponseType ResponseType
            {
                get { return _responseType; }
                set { _responseType = value; }
            }

            /// <summary> 
            /// Token is the token value that would be returned/provided by a service provider implementing the OAuth routine. Currently myspace doesn't use this method. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string Token
            {
                get { return _token; }
                set { _token = value; }
            }

            /// <summary> 
            /// TokenSecret is the token secret that would be returned/provided by a service provider implementing the OAuth routine. Currently myspace doesn't use this method. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string TokenSecret
            {
                get { return _tokenSecret; }
                set { _tokenSecret = value; }
            }

            /// <summary> 
            /// UserId would be the myspace user ID of the person who has your application installed and provides the ability use the API to query their account 
            /// from an external system, such as a website you create. 
            /// </summary> 
            /// <value>String</value> 
            /// <returns>String</returns> 
            /// <remarks></remarks> 
            public string UserId
            {
                get { return _userId; }
                set { _userId = value; }
            }

            /// <summary> 
            /// UrlParameters is used to facilitate the ability to add additional parameters to the URL. This method might be changed in the future to be more robust. 
            /// Currently it is only necessary to enable the paging ability with a GetFriends response. 
            /// </summary> 
            /// <value>Hashtable</value> 
            /// <returns>Hashtable</returns> 
            /// <remarks></remarks> 
            public Hashtable UrlParameters
            {
                get { return _urlParameters; }
                set { _urlParameters = value; }
            }

        }

        /// <summary> 
        /// MakeRequest configures a request to be posted to the Myspace REST API interface based on the parameters passed in. 
        /// </summary> 
        /// <param name="myspaceRequest">MyspaceRequest property is used to hold all the necessary configuration values for a myspace rest api request.</param> 
        /// <returns>String - XML or JSON</returns> 
        /// <remarks>This method will be called internally and there will be a separate helper class to parse the response.</remarks> 
        public string MakeRequest(MyspaceRequest myspaceRequest)
        {

            string ReturnValue;
            HttpWebRequest webRequest;
            StreamReader responseReader;
            StreamWriter requestWriter;
            string responseData = "";

            if ((myspaceRequest.ResponsePageNumber != null))
            {

                myspaceRequest.UrlParameters.Add("page", myspaceRequest.ResponsePageNumber);
                myspaceRequest.UrlParameters.Add("page_size", myspaceRequest.ResponsePageSize);
                if (myspaceRequest.ResponseListType.ToString().ToLower() != "none")
                {
                    myspaceRequest.UrlParameters.Add("list", myspaceRequest.ResponseListType.ToString());
                }

            }

            string methodPath = DefineRequestPath(myspaceRequest.RequestMethod.ToString(), myspaceRequest.AssetID);
            string requestUrl = BuildRequestURL(myspaceRequest.APIversion, myspaceRequest.BaseApiUrlHost, myspaceRequest.ResponseType, myspaceRequest.UserId, methodPath);
            string postData = BuildParameters(myspaceRequest.APIversion, requestUrl, myspaceRequest.ConsumerKey, myspaceRequest.ConsumerSecret, myspaceRequest.Token, myspaceRequest.TokenSecret, "GET", myspaceRequest.UrlParameters);
            CookieContainer cookies = new CookieContainer();
            webRequest = (HttpWebRequest)WebRequest.Create(requestUrl + postData);
            webRequest.Method = "GET";
            webRequest.ContentType = "application/x-www-form-urlencoded";
            //webRequest.CookieContainer = cookies 

            //Eventually we might implement post data once myspace has provided 
            //methods that work with post, currently there are only retrieve/get methods. 
            //------------------------------------------------------------- 
            //requestWriter = New StreamWriter(webRequest.GetRequestStream) 
            //requestWriter.Write(postData) 
            //requestWriter.Close() 

            try
            {
                responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
                responseData = responseReader.ReadToEnd();
                responseReader.Close();
                webRequest.GetResponse().Close();
            }
            catch (Exception ex)
            {
                responseData = ex.Message;
            }

            ReturnValue = responseData;

            return ReturnValue;
        }

        /// <summary> 
        /// BuildRequestUrl is a private method used as helper to create the base url. 
        /// </summary> 
        /// <param name="APIversion">Version of the Myspace API</param> 
        /// <param name="BaseUrl">BaseUrl is the root hostname for the request.</param> 
        /// <param name="ResponseTy">ResponseTy is the extension for the respone (JSON or XML)</param> 
        /// <param name="UserId">UserId is the user id of the user to retrieve information/data from.</param> 
        /// <param name="MethodPath">MethodPath determines the specific service endpoint to make the request.</param> 
        /// <returns>String</returns> 
        /// <remarks></remarks> 
        private string BuildRequestURL(string APIversion, string BaseUrl, ResponseType ResponseTy, string UserId, string MethodPath)
        {
            StringBuilder ReturnValue = new StringBuilder();

            ReturnValue.Append("http://");
            ReturnValue.Append(BaseUrl + "/");
            ReturnValue.Append(APIversion + "/");
            ReturnValue.Append("users/");
            ReturnValue.Append(UserId);
            ReturnValue.Append(MethodPath);
            ReturnValue.Append("." + ResponseTy.ToString().ToLower());

            return ReturnValue.ToString();
        }

        /// <summary> 
        /// BuildParameters is a private method used as helper to create the querystring values for the request. This method utilizes the OAuth class developed by Eran Sandler (http://eran.sandler.co.il/). 
        /// </summary> 
        /// <param name="APIversion">Version of the Myspace API</param> 
        /// <param name="requestUrl">Request URL is the url to make the request to.</param> 
        /// <param name="consumerKey">ConsumerKey is the key provided to you once you have created an application inside of the Myspace Developer Platform (MDP).</param> 
        /// <param name="consumerSecret">ConsumerSecret is the secret provided to you once you have created an application inside of the Myspace Developer Platform (MDP).</param> 
        /// <param name="Token">Token is the token value that would be returned/provided by a service provider implementing the OAuth routine. Currently myspace doesn't use this method.</param> 
        /// <param name="TokenSecret">TokenSecret is the token value that would be returned/provided by a service provider implementing the OAuth routine. Currently myspace doesn't use this method.</param> 
        /// <param name="MethodType">MethodType is the type for the method (GET, POST, DELETE etc.) currently only GET is supported by MDP.</param> 
        /// <param name="UrlParameters">UrlParameters are the additionaly parameters required for certain API calls.</param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        private string BuildParameters(string APIversion, string requestUrl, string consumerKey, string consumerSecret, string Token, string TokenSecret, string MethodType, Hashtable UrlParameters)
        {
            OAuth.OAuthBase OAuth1 = new OAuth.OAuthBase();
            StringBuilder addParams = new StringBuilder();

            if (UrlParameters.Count > 0)
            {
                int icount = 1;
                foreach (DictionaryEntry item in UrlParameters)
                {
                    if (icount == 1)
                    {
                        addParams.Append("?" + item.Key.ToString() + "=");
                        addParams.Append(OAuth1.UrlEncode(item.Value.ToString()));
                    }
                    else
                    {
                        addParams.Append("&" + item.Key.ToString() + "=");
                        addParams.Append(OAuth1.UrlEncode(item.Value.ToString()));
                    }
                    icount += 1;
                }
            }

            Uri uri1 = new Uri(requestUrl + addParams.ToString());
            string nonce = System.Guid.NewGuid().ToString();
            string timestamp = OAuth1.GenerateTimeStamp();
            string signature = OAuth1.GenerateSignature(uri1, consumerKey, consumerSecret, Token, TokenSecret, MethodType, timestamp, nonce, OAuth.OAuthBase.SignatureTypes.HMACSHA1);
            StringBuilder ReturnValue = new StringBuilder();

            ReturnValue.Append("?oauth_consumer_key=");
            ReturnValue.Append(OAuth1.UrlEncode(consumerKey));
            ReturnValue.Append("&oauth_nonce=");
            ReturnValue.Append(OAuth1.UrlEncode(nonce));
            ReturnValue.Append("&oauth_signature=");
            ReturnValue.Append(OAuth1.UrlEncode(signature));
            ReturnValue.Append("&oauth_signature_method=HMAC-SHA1");
            ReturnValue.Append("&oauth_timestamp=");
            ReturnValue.Append(timestamp);
            ReturnValue.Append("&oauth_token=");
            ReturnValue.Append(Token);
            ReturnValue.Append("&oauth_version=");
            ReturnValue.Append(GetNumericAPIVersion(APIversion));
            if (UrlParameters.Count > 0)
            {
                foreach (DictionaryEntry item in UrlParameters)
                {
                    ReturnValue.Append("&" + item.Key.ToString() + "=");
                    ReturnValue.Append(OAuth1.UrlEncode(item.Value.ToString()));
                }
            }

            return ReturnValue.ToString();
        }

        /// <summary> 
        /// Defines the request path based on the input values. 
        /// </summary> 
        /// <param name="RequestMethodType">RequestMethodType is the requestmethod to determine the right endpoint address.</param> 
        /// <param name="OptionalAssetId">OptionAssetId is used to provide additional information about specific calls (such as: GetAlbumById).</param> 
        /// <returns>String</returns> 
        /// <remarks></remarks> 
        private string DefineRequestPath(string RequestMethodType, string OptionalAssetId)
        {
            string ReturnValue = "";

            switch (RequestMethodType.ToLower())
            {
                case "getprofile":
                    ReturnValue = "/profile";
                    break;
                case "getbasicprofile":
                    ReturnValue = "";
                    break;
                case "getfriends":
                    ReturnValue = "/friends";
                    break;
                case "getfriendbyid":
                    ReturnValue = "/friends/" + OptionalAssetId;
                    break;
                case "getalbums":
                    ReturnValue = "/albums";
                    break;
                case "getalbumbyid":
                    ReturnValue = "/albums/" + OptionalAssetId;
                    break;
                case "getalbumphotosbyid":
                    ReturnValue = "/albums/" + OptionalAssetId + "/photos";
                    break;
                case "getuserphotos":
                    ReturnValue = "/photos";
                    break;
                case "getphotobyid":
                    ReturnValue = "/photos/" + OptionalAssetId;
                    break;
                case "getuserinterests":
                    ReturnValue = "/interests";
                    break;
                case "getuserdetails":
                    ReturnValue = "/details";
                    break;
                case "getuservideos":
                    ReturnValue = "/videos";
                    break;
                case "getvideobyid":
                    ReturnValue = "/videos/" + OptionalAssetId;
                    break;
                case "getuserstatus":
                    ReturnValue = "/status";
                    break;
                case "getusermood":
                    ReturnValue = "/mood";
                    break;
                default:
                    ReturnValue = "";
                    break;
            }

            return ReturnValue;
        }

        /// <summary> 
        /// GetNumericAPIVersion is a helper function to provide the API version in a numeric/double string format. 
        /// </summary> 
        /// <param name="version">Version is the string version value.</param> 
        /// <returns>String</returns> 
        /// <remarks>This method is more than likely not a good way to determine this value. Expect it to change in the future.</remarks> 
        private string GetNumericAPIVersion(string version)
        {
            string ReturnValue = "";

            switch (version)
            {
                case "v1":
                    ReturnValue = "1.0";
                    break;
                default:
                    ReturnValue = "1.0";
                    break;
            }

            return ReturnValue;
        }

    }
}
